import bpy
from typing import List
from bpy.types import Object, Operator, Context
from bpy.props import StringProperty
from ...libs.functions.get_common_vars import get_common_vars
from ...libs.functions.animation import anim_attrs
from ...libs.functions.basics import ocultar_post_panel_settings
from ...addon.naming import FluidLabNaming


class FLUIDLAB_OT_fs_animation_button(Operator):
    bl_idname = "fluidlab.fs_animation_button"
    bl_label = "Button"
    bl_description = "Animation Button"
    bl_options = {"REGISTER", "UNDO"}

    call_from: StringProperty(default="")


    def reset_control_props(self, props, type_button, call_from) -> None:

        # Seteamos los estados de los booleans de control:
        if type_button.lower() == "add":
            setattr(props, call_from + "_animed", True)

        if type_button.lower() == "remove":
            setattr(props, call_from + "_animed", False)
        
        ocultar_post_panel_settings()
    

    def update_button(self, type_props, call_from) -> None:
        # ejecutamos este mismo operador pero primero hacemos un remove y luego un add:
        bpy.ops.fluidlab.fs_animation_button(call_from="Remove" +"."+ type_props +"."+ call_from)
        bpy.ops.fluidlab.fs_animation_button(call_from="Add" +"."+ type_props +"."+ call_from)


    def exception_viscoelastic_springs(self, props, emitters:List[Object], type_button, type_props, call_from) -> None:

        #------------------------------------#
        # Exception use_viscoelastic_springs #
        #------------------------------------#

        condition = props.use_viscoelastic_springs_from_to == 'ON_OFF'
        _from = True if condition else False
        _to = False if condition else True
        # todas las properties son de grupo excepto la excepcion de Geometry Emitter el Switcher.
        
        fluid_props = ["use_viscoelastic_springs"]
        effector_weights = FluidLabNaming.weights
        
        _start = getattr(props, call_from + "_start")
        
        for emitter_ob in emitters:

            psys = emitter_ob.particle_systems.active

            if type_button.upper() == 'ADD':
                
                add_dpath = call_from
                
                if call_from in fluid_props:
                    # Si son propiedades de fluid props necesita buscarlas en settings.fluid:
                    p_settings = psys.settings.fluid
                elif call_from in effector_weights:
                    # Si son propiedades de effector_weights props necesita buscarlas en settings.effector_weights:
                    p_settings = psys.settings.effector_weights
                else: 
                    p_settings = psys.settings

                data =  {
                            'object':  p_settings, 
                            'dpath':   add_dpath, 
                            'frame':   _start,
                            'value':   _from
                }
                anim_attrs(data)

                data =  {
                            'object':  p_settings, 
                            'dpath':   add_dpath, 
                            'frame':   getattr(props, call_from + "_start")+1,
                            'value':   _to
                }
                anim_attrs(data)

            elif type_button.upper() == 'REMOVE':

                rm_dpath = "fluid." + call_from

                # Las fcurves estan en los settings nunca en .fluid, por eso usamos .settings:
                p_settings = psys.settings

                if not p_settings.animation_data:
                    continue
                
                action = p_settings.animation_data.action
                if not action:
                    continue

                if not p_settings.animation_data.action.fcurves:
                    continue                

                fcurve = action.fcurves.find(data_path=rm_dpath, index=0)
                if not fcurve:
                    continue
                
                action.fcurves.remove(fcurve)
            
            elif type_button.upper() == 'UPDATE':
                self.update_button(type_props, call_from)
        
        self.reset_control_props(props, type_button, call_from)
        
        return {'FINISHED'}
    

    def exception_geometry_emitter(self, props, emitters_list, type_button, call_from):

        #------------------------------------#
        #    Exception geometry emitter      #
        #------------------------------------#
            
        # Necesito otras props diferentes:
        emitter_ob = emitters_list.get_current_emitter
        
        _switch = getattr(props, call_from + "_switch")
        possibilities = ["render", "viewport"]

        if type_button.upper() == 'ADD':

            def anim_show_instancer_for(target_ob: Object, target:str, frame:int, values:List[bool]) -> None:

                add_dpath = "show_instancer_for_" + target
    
                data =  {
                        'object':  target_ob, 
                        'dpath':   add_dpath,
                        'frame':   frame,
                        'value':   values[0]
                }
                anim_attrs(data)

                data =  {
                            'object':  target_ob, 
                            'dpath':   add_dpath, 
                            'frame':   frame+1,
                            'value':   values[1]
                }
                anim_attrs(data)
            
            # emitter_ob.show_instancer_for_render y emitter_ob.show_instancer_for_viewport:
            for target in possibilities:
                anim_show_instancer_for(target_ob=emitter_ob, target=target, frame=_switch, values=[True, False])

            # cambiamos el estado de animed (para el boton rm en rojo):
            setattr(props, call_from + "_animed", True)
        
        elif type_button.upper() == "REMOVE":

            if not emitter_ob.animation_data:
                print(f"{emitter_ob.name} not have animation_data!")
                return {'CANCELLED'}
            
            action = emitter_ob.animation_data.action
            if not action:
                print(f"{emitter_ob.name} not have animation_data.action!")
                return {'CANCELLED'}

            if not emitter_ob.animation_data.action.fcurves:
                print(f"{emitter_ob.name} not have animation_data.action.fcurves!")
                return {'CANCELLED'}

            for target in possibilities:
                
                rm_dpath = "show_instancer_for_" + target

                # Eliminamos las animaciones:
                fcurve = action.fcurves.find(data_path=rm_dpath, index=0)
                if not fcurve:
                    continue
                
                action.fcurves.remove(fcurve)

                # las dejamos en False que es como estaban:
                setattr(emitter_ob, rm_dpath, False)
            
            # cambiamos el estado de animed (para el boton rm en rojo):
            setattr(props, call_from + "_animed", False)

        return {'FINISHED'}
    

    def object_anim_factor_x(self):
        print("Aquí se animaría solo un eje de object_anim_factor")        
        return {'FINISHED'}
    
    
    def execute(self, context):

        fluid_groups = get_common_vars(context, get_fluid_groups=True)
        active_group = fluid_groups.active
        emitters_list = active_group.emitters
        active_emitter_item = emitters_list.active

        # emitters = emitters_list.get_all_emitters
        
        # Ahora va por emitter no por grupo:
        emitter_ob = emitters_list.get_current_emitter
        emitters = [emitter_ob]

        type_button, type_props, call_from = self.call_from.split(".")
        # print(type_button, type_props, call_from) # -> por ejemplo: Add wights boid, Add physics timestep

        if type_props == "physics":
            props = getattr(active_emitter_item, type_props).anim
        
        elif type_props == "springs":
            props = getattr(active_emitter_item, type_props).anim
        
        elif type_props == "weights":
            props = getattr(active_emitter_item, type_props)
        
        elif type_props == "geometry_emitter":
            props = getattr(active_emitter_item, type_props)

        # para la escepción de animar el use_viscoelastic_springs:
        if call_from == "use_viscoelastic_springs":
            return self.exception_viscoelastic_springs(props, emitters, type_button, type_props, call_from)
            
        # para la excepción de animar el Geometry Emitter con el Switch at Frame:
        if type_props == "geometry_emitter":
            return self.exception_geometry_emitter(props, emitters_list, type_button, call_from)
        
        # para la excepción de object_anim_factor_x 
        if call_from == "object_anim_factor_x":
            # -- Sin hacer --
            return self.object_anim_factor_x()        

        fluid_props = ["spring_force", "stiffness", "linear_viscosity", "use_viscoelastic_springs", "fluid_radius"]
        effector_weights = FluidLabNaming.weights

        _from = getattr(props, call_from + "_from")
        _to = getattr(props, call_from + "_to")
        _start = getattr(props, call_from + "_start")
        _increase = getattr(props, call_from + "_increase")
        _duration = getattr(props, call_from + "_duration")
        _decrease = getattr(props, call_from + "_decrease")    
        _increase_decrease = getattr(props, call_from + "_inc_dec")

        for emitter_ob in emitters:

            psys = emitter_ob.particle_systems.active

            if type_button.upper() == 'ADD':
                
                if call_from in fluid_props:
                    # Si son propiedades de fluid props necesita buscarlas en settings.fluid:
                    p_settings = psys.settings.fluid
                elif call_from in effector_weights:
                    # Si son propiedades de effector_weights props necesita buscarlas en settings.effector_weights:
                    p_settings = psys.settings.effector_weights
                else: 
                    p_settings = psys.settings

                data =  {
                            'object':  p_settings, 
                            'dpath':   call_from, 
                            'frame':   _start,
                            'value':   _from
                }
                anim_attrs(data)

                if _increase_decrease:
                    data =  {
                                'object':  p_settings, 
                                'dpath':   call_from, 
                                'frame':   _start + _increase,
                                'value':   _to
                    }
                    anim_attrs(data)

                data =  {
                            'object':  p_settings, 
                            'dpath':   call_from, 
                            'frame':   getattr(props, call_from + "_start") + getattr(props, call_from + "_duration"),
                            'value':   _to
                }
                anim_attrs(data)

                if _increase_decrease:
                    data =  {
                                'object':  p_settings, 
                                'dpath':   call_from, 
                                'frame':   _start + _duration + _decrease,
                                'value':   _from
                    }
                    anim_attrs(data)

            elif type_button.upper() == 'REMOVE':

                # Las fcurves estan en los settings nunca en .fluid, por eso usamos .settings:
                p_settings = psys.settings

                if not p_settings.animation_data:
                    continue
                
                action = p_settings.animation_data.action
                if not action:
                    continue

                if not p_settings.animation_data.action.fcurves:
                    continue

                if call_from in fluid_props:
                    # Si son propiedades de fluid props necesita los dpath asi fluid.propiedad:
                    dpath = "fluid." + call_from 
                elif call_from in effector_weights:
                    # Si son propiedades de effector_weights props necesita los dpath asi effector_weights.propiedad:
                    dpath = "effector_weights." + call_from
                else: 
                    dpath = call_from

                fcurve = action.fcurves.find(data_path=dpath, index=0)
                if not fcurve:
                    continue
                
                action.fcurves.remove(fcurve)
            
            elif type_button.upper() == 'UPDATE':
                self.update_button(type_props, call_from)

        self.reset_control_props(props, type_button, call_from)
        return {'FINISHED'}